package com.kongt.download;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 分配下载任务的线程
 */
@Component
@Slf4j
public class DownTaskManager {

    @Autowired
    DownloadConfig downloadConfig;

    public void download(FutureTask<Runnable>... afterTasks) throws IOException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(downloadConfig.getThreadC());
        String downloadUrl = downloadConfig.getDownloadUrl();
        File destFile = getDestFile(downloadUrl);
        //下载文件总长度
        long downloadLength = getDownloadLength(downloadUrl);

        //已经下载文件的长度
        AtomicLong hasDownLength = new AtomicLong();

        //下载进度统计
        AsyncHandler<Integer> asyncHandler = (j) -> {
            //总下载长度
            long l = hasDownLength.addAndGet(j);
            log.info("download percent:{}%", BigDecimal.valueOf(l * 100.0).divide(BigDecimal.valueOf(downloadLength), 2, BigDecimal.ROUND_HALF_UP));
        };
        //闭锁的使用
        CountDownLatch countDownLatch = new CountDownLatch(downloadConfig.getThreadC());
        LocalDateTime downStart = LocalDateTime.now();
        if (downloadLength != -1) {
            //最后下载的长度
            long averageLength = downloadLength / downloadConfig.getThreadC();
            for (int i = 1; i <= downloadConfig.getThreadC(); i++) {
                long start = (i - 1) * averageLength;
                long end = i * averageLength >= downloadLength ? downloadLength : i * averageLength;
                executorService.execute(new BlockDownloaderTask(destFile, start, end, downloadUrl, asyncHandler, countDownLatch, 5));
            }
            //等待执行完成
            countDownLatch.await();
            LocalDateTime downEnd = LocalDateTime.now();
            Duration durtion = Duration.between(downStart, downEnd);
            BigDecimal speed = BigDecimal.valueOf(downloadLength)
                    .divide(BigDecimal.valueOf(durtion.getSeconds()), 4, BigDecimal.ROUND_HALF_UP)
                    .divide(BigDecimal.valueOf(1048576), 4, BigDecimal.ROUND_HALF_UP);
            log.info("download the dest file:{}", destFile);
            log.info("download durtion:{}s,speed:{}Mb/s", durtion.getSeconds(), speed);
            if (null != afterTasks) {
                for (FutureTask<Runnable> task : afterTasks) {
                    executorService.execute(task);
                }
            }
        }
        executorService.shutdown();
    }

    private File getDestFile(String url) {
        String extension = getFileExtensionFromUrl(url);
        File dir = new File(downloadConfig.getDownloadPath());
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return new File(dir, String.format("%d%s", System.currentTimeMillis(), extension));
    }

    private String getFileExtensionFromUrl(String url) {
        int i = url.lastIndexOf(".");
        return -1 == i || url.length() - i > 5 ? ".download" : url.substring(i);

    }

    private long getDownloadLength(String downloadUrl) {
        long length = -1;
        try {
            //获得下载文件的总长度
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            // 配置信息
            RequestConfig requestConfig = RequestConfig.custom()
                    // 设置连接超时时间(单位毫秒)
                    .setConnectTimeout(5000)
                    // 设置请求超时时间(单位毫秒)
                    .setConnectionRequestTimeout(5000)
                    // socket读写超时时间(单位毫秒)
                    .setSocketTimeout(5000)
                    // 设置是否允许重定向(默认为true)
                    .setRedirectsEnabled(false).build();
            HttpGet httpGet = new HttpGet(downloadUrl);
            httpGet.setConfig(requestConfig);
            CloseableHttpResponse response = httpClient.execute(httpGet);
            int code = response.getStatusLine().getStatusCode();
            if (code == HttpStatus.SC_OK) {
                length = response.getEntity().getContentLength();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return length;
    }


}
